home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 2000 August: Tool Chest / Dev.CD Aug 00 TC Disk 2.toast / pc / sample code / overview / dtscpluslibrary / sources / collectionclasses.cp < prev    next >
Encoding:
Text File  |  2000-06-23  |  17.2 KB  |  604 lines

  1. /*
  2.     File:        CollectionClasses.cp
  3.  
  4.     Contains:    The following collection classes are implemented: TLinkedList, TStack, (TQueue, TDeQueue),
  5.                   THashTable.
  6.                   CollectionClasses.cp contains the collection class member functions. 
  7.  
  8.     Written by: Kent Sandvik    
  9.  
  10.     Copyright:    Copyright © 1992-1999 by Apple Computer, Inc., All Rights Reserved.
  11.  
  12.                 You may incorporate this Apple sample source code into your program(s) without
  13.                 restriction. This Apple sample source code has been provided "AS IS" and the
  14.                 responsibility for its operation is yours. You are not permitted to redistribute
  15.                 this Apple sample source code as "Apple sample source code" after having made
  16.                 changes. If you're going to re-distribute the source, we require that you make
  17.                 it clear in the source that the code was descended from Apple sample source
  18.                 code, but that you've made changes.
  19.  
  20.     Change History (most recent first):
  21.                 8/18/1999    Karl Groethe    Updated for Metrowerks Codewarror Pro 2.1
  22.                 
  23.  
  24. */
  25. #ifndef _COLLECTION_
  26. #include "CollectionClasses.h"
  27. #endif
  28.  
  29.  
  30. // _________________________________________________________________________________________________________ //
  31. //    TLINKEDLIST Class definitions.
  32.  
  33. //    CONSTRUCTORS AND DESTRUCTORS
  34. #pragma segment Collections
  35. TLinkedList::TLinkedList(short max)
  36. // Create a Linked list class (max entries pre-defined in a constant in the header file).
  37. {
  38.     fMaxCollectionSize = max;                    // keep track of how many entries we could add to the collection
  39.     fCollectionSize = 0;                        // no entries yet
  40.     // Fix the head and the rest of the pointer hooks.
  41.     fHead = new fNODE;                            // create nodes
  42.     fTail = new fNODE;
  43.     fLastEntry = new fNODE;
  44.     fCurrentNode = new fNODE;
  45.  
  46.     fHead->fNext = fTail;                        // default head points at tail, then we will push in nodes between
  47.     fHead->fPrevious = fHead;                    // bit your own tail
  48.     fTail->fNext = fTail;                        // bite your own tail
  49.     fTail->fPrevious = fHead;                    // hook tail with head (double linked list)
  50.     fTail->fKey = NULL;                            // mark the last entry as NULL
  51.     fHead->fKey = NULL;                            // mark the first entry as NULL
  52.     fLastEntry->fPrevious = fHead;                // hook the ptr node between the head
  53.     fLastEntry->fNext = fTail;                    // …and the tail
  54. }
  55.  
  56.  
  57. #pragma segment Collections
  58. TLinkedList::~TLinkedList()
  59. // Destruct the structures required for the former linked list.
  60. {
  61.     struct fNODE* temp = fHead;                    // create a temp node
  62.  
  63.     while (temp != fTail)
  64.         // while we have entries, delete them from head to tail
  65.         {
  66.             fHead = temp;                        // copy temp node to the head position
  67.             temp = temp->fNext;                    // and make temp point at the next one
  68.             delete fHead;                        // meanwhile delete the head, and continue copying tmp to head
  69.         }
  70. }
  71.  
  72.  
  73. //    MAIN INTERFACES
  74.  
  75. #pragma segment Collections
  76. Boolean TLinkedList::Append(const TItemtype item)
  77. // Append entry to the linked list (at the end).
  78. {
  79.     if (fCollectionSize < fMaxCollectionSize)
  80.     {
  81.         struct fNODE*        temp = new fNODE;    // create a new node
  82.         temp->fKey = item;
  83.  
  84.         // Hook the temp node between the last entry and the tail        
  85.         if (fCollectionSize == 0)                // first entry?
  86.         {
  87.             fHead->fNext = temp;                // hook it just after the head
  88.             temp->fNext = fTail;                // and before the tail
  89.             fLastEntry = temp;                    // and keep track of it!
  90.         }
  91.         else                                    // OK now we will track of the fCurrentNode
  92.             {
  93.                 temp->fNext = fTail;            // stuck it just before the tail
  94.                 fLastEntry->fNext = temp;        // and after the current node
  95.                 fLastEntry = temp;                // and mark it the current node
  96.             }
  97.  
  98.         fCollectionSize++;                        // keep a count on the size of the linked list
  99.         goto AppendOK;
  100.     }
  101.     ASSERT(fCollectionSize < fMaxCollectionSize, "\pOverflow of the LinkedList");
  102.     return false;
  103. AppendOK:return true;
  104. }
  105.  
  106.  
  107. #pragma segment Collections
  108. Boolean TLinkedList::Remove(const TItemtype item)
  109. // Remove defined entry from the linked list.
  110. {
  111.     // First find the item, if found remove it, connect the links and decrease the collection size count
  112.     this->Reset();                                // get back to the first entry
  113.     struct fNODE*            temp = fHead;        // keep a temp pointer to earlier node passed
  114.  
  115.     do                                            // walk along the list    
  116.     {
  117.         if (fCurrentNode->fKey == item)            // found a hit?    
  118.         {
  119.             // yes!
  120.             temp->fNext = fCurrentNode->fNext;    // short circuit the current node
  121.             delete fCurrentNode;                // delete this node
  122.             fCollectionSize--;                    // decreate the collection count
  123.             goto OK;                            // and jump out 
  124.         }
  125.  
  126.         temp = fCurrentNode;                    // keep track of the just visited node
  127.         fCurrentNode = fCurrentNode->fNext;        // …and step forward
  128.     } while (fCurrentNode != fTail);            // as long as we are inside the linked list    
  129.  
  130.     return false;                                // nothing happened…
  131. OK:    return true;                                // OK, we were able to delete the entry
  132. }
  133.  
  134.  
  135. #pragma segment Collections
  136. Boolean TLinkedList::IsEmpty()
  137. // Check if the collection is empty (head points at tail).
  138. {
  139.     return (fHead->fNext == fTail);                // check if the next pointer is pointing on the tail
  140. }
  141.  
  142.  
  143. #pragma segment Collections
  144. Boolean TLinkedList::Find(const TItemtype item)
  145. // Find if we have the itemtype in the 'queue'.
  146. {
  147.     TItemtype tempVal;
  148.     this->Reset();                                // get back to the first entry
  149.  
  150.     while ((tempVal = this->Next()) != NULL)
  151.         // get all entries one at a time
  152.         {
  153.             if (tempVal == item)                // if we found a one-to-one relationship
  154.                 return true;                    // signal OK
  155.         }
  156.     return false;                                // otherwise signal false
  157. }
  158.  
  159.  
  160. #pragma segment Collections
  161. Size TLinkedList::GetSize() const
  162. // Return the internal count of entries in collection.
  163. {
  164.     return fCollectionSize;
  165. }
  166.  
  167.  
  168. // ITERATORS
  169. #pragma segment Collections
  170. TItemtype TLinkedList::First()
  171. // Get the first entry in the collection.
  172. {
  173.     if (fCollectionSize != 0)
  174.         return fHead->fNext->fKey;                // return what FHead->fNext has (the first 'real' node)
  175.     else
  176.         return NULL;
  177. }
  178.  
  179.  
  180. #pragma segment Collections
  181. TItemtype TLinkedList::Next()
  182. // Return next entry in the list.
  183. {
  184.     TItemtype item;
  185.     item = fCurrentNode->fKey;                    // get current item
  186.     fCurrentNode = fCurrentNode->fNext;            // then increase the node to the next entry
  187.  
  188.     return item;                                // return item
  189. }
  190.  
  191.  
  192. #pragma segment Collections
  193. TItemtype TLinkedList::Last()
  194. // Return last entry in the list.
  195. {
  196.     // because we cached the first entry, it's easy to return it
  197.     // we might have problems with a real de-queue later if 
  198.     // we don't revise this scheme (but most likely we will override Last() with new behavior
  199.     return fLastEntry->fKey;                    // the fLastNode is always pointing at the last entry
  200. }
  201.  
  202.  
  203. #pragma segment Collections
  204. void TLinkedList::Reset()
  205. // Make the current node point at the first entry from beginning (reset).
  206. {
  207.     fCurrentNode = fHead->fNext;                // reset the fCurrentNode pointer to the first entry
  208. }
  209.  
  210.  
  211. // _________________________________________________________________________________________________________ //
  212. //    TSTACK Class definitions.
  213.  
  214. //    CONSTRUCTORS AND DESTRUCTORS
  215. #pragma segment Collections
  216. TStack::TStack(short max)
  217. // Create a stack class (max pre-defined in the header files as a constant).
  218. {
  219.     fMaxCollectionSize = max;                    // keep track of how big we could grow the stack into
  220.     fCollectionSize = 0;                        // no entries yet
  221. }
  222.  
  223.  
  224. #pragma segment Collections
  225. TStack::~TStack()
  226. // Default constructor -- not used for the time being.
  227. {
  228. }
  229.  
  230.  
  231. //    MAIN INTERFACES
  232. #pragma segment Collections
  233. Boolean TStack::Push(const TItemtype item)
  234. // Push entry into the stack (beginning).
  235. {
  236.     if (fCollectionSize < fMaxCollectionSize)    // yes, we bail out and don't add more entries
  237.     {
  238.         struct fNODE* temp = new fNODE;            // create temp node
  239.         temp->fKey = item;                        // store value in temp node
  240.         temp->fNext = fHead->fNext;                // make temp point at the real next block 
  241.         fHead->fNext = temp;                    // and make temp suddenly the real head (stack head!)
  242.         fCollectionSize++;                        // increase the stack size
  243.         if (fCollectionSize == 1)                // first entry?
  244.             fLastEntry = temp;                    // store it for fast access later (cache the ptr)
  245.  
  246.         goto PushOK;
  247.     }
  248.     ASSERT(fCollectionSize < fMaxCollectionSize, "\pOverflow of the Stack");// signal about a possible problem
  249.     return false;
  250. PushOK:return true;
  251. }
  252.  
  253.  
  254. #pragma segment Collections
  255. TItemtype TStack::Pop()
  256. // Pop entry from the stack (beginning).
  257. {
  258.     TItemtype item;
  259.     struct fNODE* temp = fHead->fNext;            // create temp node with the header next pointer values
  260.     fHead->fNext = temp->fNext;                    // copy back the following pointer from temp to the head
  261.     item = temp->fKey;                            // get the temp item
  262.  
  263.     delete temp;                                // delete the suddenly non-needed node
  264.     fCollectionSize--;                            // decrease the stack
  265.     return item;                                // return the asked value
  266. }
  267.  
  268.  
  269. #pragma segment Collections
  270. Boolean TStack::Append(const TItemtype item)
  271. // Append entry, same as Push, but we included it.
  272. {
  273.     return (this->Push(item));                    // call the one and only method supported with stack insertion
  274. }
  275.  
  276.  
  277. #pragma segment Collections
  278. Boolean TStack::Remove(const TItemtype            /*item*/)
  279. // Remove is not implemented, but we had to include it because we are subclassing from a TLinkedList.
  280. {
  281.     return false;                                // not supported
  282. }
  283.  
  284.  
  285. // _________________________________________________________________________________________________________ //
  286. //    TQUEUE
  287. //    CONSTRUCTORS AND DESTRUCTORS
  288. #pragma segment Collections
  289. TQueue::TQueue(short max)
  290. // Create a queue class (max pre-defined in the header files as a constant).
  291. {
  292.     fMaxCollectionSize = max;                    // keep track of how big we could grow the stack into
  293.     fCollectionSize = 0;                        // no entries yet
  294. }
  295.  
  296.  
  297. #pragma segment Collections
  298. TQueue::~TQueue()
  299. // Default destructor, not used for the time being.
  300. {
  301. }
  302.  
  303.  
  304. //    MAIN INTERFACE
  305. #pragma segment Collections
  306. Boolean TQueue::Put(const TItemtype item)
  307. // Put entry into the queue (beginning).
  308. {
  309.     if (fCollectionSize < fMaxCollectionSize)    // no problems?
  310.     {
  311.         struct fNODE* temp = new fNODE;            // create a new node
  312.         temp->fKey = item;                        // add value to the node bucket    
  313.  
  314.         // place it in the beginning of the queue
  315.         temp->fNext = fHead->fNext;                // push it into the queue
  316.         fHead->fNext = temp;                    // now it's there
  317.  
  318.         // update the tail pointer
  319.         if (fCollectionSize == 0)                // first entry?
  320.         {
  321.             fTail->fPrevious = temp;            // hook fTail to the entry
  322.         }
  323.         temp->fPrevious = fHead;                // and hook it back to head
  324.         fLastEntry->fPrevious = temp;            // and as well as to the current first entry
  325.         fLastEntry = temp;                        // and mark it as the current first entry    
  326.  
  327.         fCollectionSize++;                        // increase the collection count
  328.         goto QueueOK;
  329.     }
  330.     ASSERT(fCollectionSize < fMaxCollectionSize, "\pOverflow of TQueue");
  331.     return false;
  332. QueueOK:return true;
  333. }
  334.  
  335.  
  336. #pragma segment Collections
  337. Boolean TQueue::Append(const TItemtype item)
  338. // Append is the same as Put.
  339. {
  340.     return (this->Put(item));                    // call TQueue.Put
  341. }
  342.  
  343.  
  344. #pragma segment Collections
  345. TItemtype TQueue::Get()
  346. // Get entry from the end of the list (queue).
  347. {
  348.     TItemtype item;
  349.  
  350.     // get the node and the value        
  351.     struct fNODE* temp = fTail->fPrevious;        // get the last before fTail
  352.     item = temp->fKey;                            // get the item    
  353.  
  354.  
  355.     // change the pointers
  356.     fTail->fPrevious = temp->fPrevious;            // hook fTail to the previous entry
  357.     temp->fPrevious->fNext = fTail;                // hook the node to tail as well    
  358.  
  359.     delete temp;                                // delete the entry    
  360.  
  361.     fCollectionSize--;                            // decrease the collection count    
  362.     return item;                                // return the item
  363. }
  364.  
  365.  
  366. #pragma segment Collections
  367. TItemtype TQueue::Last()
  368. // Get last entry from queue.
  369. {
  370.     return fTail->fPrevious->fKey;                // get the last entry
  371. }
  372.  
  373.  
  374. #pragma segment Collections
  375. Boolean TQueue::Remove(const TItemtype            /*item*/)
  376. // Remove is not supported, but we needed to include this because TQueue is subclassed from TLinkedList
  377. {
  378.     return false;                                // not supported
  379. }
  380.  
  381.  
  382.  
  383. // _________________________________________________________________________________________________________ //
  384. // TDEQUE
  385.  
  386. //    CONSTRUCTORS AND DESTRUCTORS
  387. #pragma segment Collections
  388. TDeque::TDeque(short max)
  389. // Create dequeue class (max pre-defined in the header files as a constant).
  390. {
  391.     fMaxCollectionSize = max;                    // keep track of how big we could grow the stack into
  392.     fCollectionSize = 0;                        // no entries yet
  393. }
  394.  
  395.  
  396. #pragma segment Collections
  397. TDeque::~TDeque()
  398. // Default destructor, not used for the time being.
  399. {
  400. }
  401.  
  402. #pragma segment Collections
  403. Boolean TDeque::Push(const TItemtype item)
  404. // Push entry into queue (beginning).
  405. {
  406.     return (TQueue::Put(item));                    // call inherited Put
  407. }
  408.  
  409.  
  410. #pragma segment Collections
  411. Boolean TDeque::PutAtEnd(const TItemtype item)
  412. // Place entry at end of the deque.
  413. {
  414.     if (fCollectionSize < fMaxCollectionSize)    // bail out if we reach the limit
  415.     {
  416.         struct fNODE*     temp = new fNODE;        // create temp node
  417.         temp->fKey = item;                        // store the value
  418.         temp->fNext = fTail;                    // point at tail
  419.         temp->fPrevious = fTail->fPrevious;        // make the backwards ptr hooked to the new entry
  420.         fTail->fPrevious->fNext = temp;            // and hook the old last one into this one 
  421.         fTail->fPrevious = temp;                // make the tail point at the new entry
  422.  
  423.         fCollectionSize++;                        // increase the counter
  424.         goto PutAtEndOK;
  425.     }
  426.     ASSERT(fCollectionSize < fMaxCollectionSize, "\pOverflow of the TDeque");
  427.     return false;
  428. PutAtEndOK:return true;
  429. }
  430.  
  431.  
  432. TItemtype TDeque::Pop()
  433. // Pop entry from the queue end.
  434. {
  435.     TItemtype item;
  436.     struct fNODE* temp = fHead->fNext;            // hook to the beginning of the deque
  437.     fHead->fNext = temp->fNext;                    // hook head to the following node
  438.     temp->fNext->fPrevious = fHead;                // and the next one should point back at the head
  439.  
  440.     item = temp->fKey;                            // get the stored value
  441.     delete temp;                                // remove node
  442.     fCollectionSize--;                            // decrease the counter
  443.  
  444.     return item;                                // and return the item
  445. }
  446.  
  447.  
  448. // _________________________________________________________________________________________________________ //
  449. // THASHTABLE 
  450.  
  451. //    CONSTRUCTORS AND DESTRUCTORS
  452. THashTable::THashTable()
  453. // Create a hash table class.
  454. {
  455.     // Clear the array bucket.
  456.     for (long i = 0; i < NBUCKETS; i++)
  457.         fBucket[i] = 0;
  458. }
  459.  
  460.  
  461. THashTable::~THashTable()
  462. // Delete any memory structures required for the hashtable class.
  463. {
  464.     // Delete the entries in the array bucket.    
  465.     for (long i = 0; i < NBUCKETS; i++)
  466.     {
  467.         THashEntryPtr p = fBucket[i];            // get a ptr to the entry
  468.         while (p)
  469.             // while a valid ptr
  470.             {
  471.                 fBucket[i] = p->fNext;            // install next entry in array
  472.                 delete p;                        // delete the current node
  473.                 p = fBucket[i];                    // ptr back to the new value
  474.             }
  475.     }
  476. }
  477.  
  478.  
  479. //    MAIN INTERFACE
  480. #pragma segment Collections
  481. Boolean THashTable::Add(THashKey key,
  482.                         TItemtype value)
  483. // Add entries to the hash table.
  484. {
  485.     THashEntryPtr p;
  486.     THashKey whichItem = this->Hash(key);        // hash out the real value
  487.  
  488.     cout << " whichItem = " << whichItem << "\n";
  489.  
  490.     p = FindInBucket(fBucket[whichItem], key);
  491.     if (p)                                        // have already an entry?
  492.         goto problem;                            // signal the failure
  493.  
  494.     p = this->AddElement(key);                    // add our entry
  495.     p->fValue = value;                            // add the item value to the entry
  496.     return true;
  497.  
  498. problem:return false;
  499. }
  500.  
  501.  
  502. #pragma segment Collections
  503. Boolean THashTable::Remove(THashKey key)
  504. // Remove entries from the hash table.
  505. {
  506.     THashKey whichItem = this->Hash(key);        // hash out the real value
  507.     THashEntryPtr p = FindInBucket(fBucket[whichItem], key);
  508.     if (!p)                                        // no valid ptr?
  509.         goto problem;
  510.  
  511.     if (fBucket[whichItem] == p)
  512.         fBucket[whichItem] = p->fNext;            // connect to next node
  513.  
  514.     delete p;                                    // delete current node
  515.     return true;
  516.  
  517. problem:return false;
  518. }
  519.  
  520.  
  521.  
  522. #pragma segment Collections
  523. TItemtype THashTable::Find(THashKey key)
  524. // Find entries in the hash table.
  525. {
  526.     THashKey whichItem = this->Hash(key);        // hash out the real value
  527.     THashEntryPtr p = this->FindInBucket(fBucket[whichItem], key);
  528.     if (!p)
  529.         return 0;                                // return 0
  530.     else
  531.         return p->fValue;                        // return valid value
  532. }
  533.  
  534.  
  535. void THashTable::MapCar(MapFun function)
  536. // Run a function on each entry in the hash table.
  537. {
  538.     for (long i = 0; i < NBUCKETS; i++)            // iterate through all the entries
  539.     {
  540.         THashEntryPtr p = fBucket[i];            // get the ptr to the entry
  541.  
  542.         while (p)
  543.             // while we have a nice value
  544.             {
  545.                 function(p);                    // call the function on p
  546.                 p = p->fNext;                    // get the next ptr
  547.             }
  548.     }
  549. }
  550.  
  551.  
  552. // PRIVATE INTERNAL FUNCTIONS
  553. THashEntryPtr THashTable::AddElement(THashKey key)
  554. // Add an element to the internal structure.
  555. {
  556.     THashKey whichItem = this->Hash(key);
  557.     THashEntryPtr p = new THashEntry(key);
  558.  
  559.     p->fNext = fBucket[whichItem];                // get ptr to next entry
  560.     if (fBucket[whichItem])                        // OK?
  561.         fBucket[whichItem]->fPrevious = p;        // establish ptrs backwards
  562.  
  563.     fBucket[whichItem] = p;                        // as well as the current one
  564.  
  565.     return p;
  566. }
  567.  
  568.  
  569. THashKey THashTable::Hash(THashKey key)
  570. // Hash out a key to be used with the buckets. Override for your own preferences.
  571. {
  572.     key &= kResourceIDMask;
  573.     return ((THashKey)(0x7FF & (key ^ (key >> 11))) % NBUCKETS);
  574.     // Note: if you write your own hash function, don't forget modula NBUCKETS to get
  575.     // down the size of the key so it hooks to the buckets.
  576. }
  577.  
  578.  
  579. THashEntryPtr THashTable::FindInBucket(THashEntryPtr p,
  580.                                        THashKey key)
  581. // Find bucket where entry is located.
  582. {
  583.     while (p)
  584.         // valid pointer?
  585.         {
  586.             if (p->fKey == key)                    // hit?
  587.                 break;                            // found it!
  588.             p = p->fNext;                        // nope, continue cycling the list
  589.         }
  590.     return p;                                    // return found (not found) ptr
  591. }
  592.  
  593.  
  594. // _________________________________________________________________________________________________________ //
  595.  
  596.  
  597. /*    Change History (most recent last):
  598.   No        Init.    Date        Comment
  599.   1            khs        11/7/92        New file
  600.   2            khs        11/28/92    Added TLinkedList
  601.   3            khs        11/29/92    Added TQueue and TDeque
  602.   4            khs        1/14/93        Cleanup
  603. */
  604.